#!/usr/bin/env python
# coding=utf-8
from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkcore.client import AcsClient
from aliyunsdkcr.request.v20160607 import GetRepoTagsRequest, DeleteImageRequest, GetRepoListByNamespaceRequest
import settings
import json
import time
import datetime
import sys
import getopt


class NamespaceCleaner:

    def __init__(self, access_key_id, access_key_secret, region_id, namespace, days_keep=10, tags_keep=10,
                 tag_prefix=""):
        self.__repo_list = list()
        self.__total = 0
        self.__page = 1
        self.__page_count = 0
        self.__page_size = 30
        self.__tag_id = 0
        self.__access_key_id = access_key_id
        self.__access_key_secret = access_key_secret
        self.__region_id = region_id
        self.__namespace = namespace
        self.__days_keep = int(days_keep)
        self.__tags_keep = int(tags_keep)
        self.__tag_prefix = tag_prefix
        self.get_full_repos()

    def get_repos(self):
        api_client = AcsClient(self.__access_key_id, self.__access_key_secret, self.__region_id)
        request = GetRepoListByNamespaceRequest.GetRepoListByNamespaceRequest()
        request.set_RepoNamespace(self.__namespace)
        request.set_endpoint("cr.%s.aliyuncs.com" % self.__region_id)
        try:
            response = api_client.do_action_with_exception(request)
            response_dict = json.loads(str(response, 'utf8'))
            if not self.__page_count:
                self.__total = response_dict['data']['total']
                self.__page_count = response_dict['data']['total'] // self.__page_size
            for r in response_dict['data']['repos']:
                self.__repo_list.append(r)
        except ServerException as e:
            if e.http_status == 404:
                print("Namespace '%s' in region '%s' is not exist." % (self.__namespace, self.__region_id))
                sys.exit(2)
            else:
                print(e)
        except ClientException as e:
            print(e)

    def get_full_repos(self):
        while True:
            self.get_repos()
            self.__page += 1
            if self.__page > self.__page_count:
                break
        print("There are %s repos in namespace %s" % (self.__total, self.__namespace))
        return self.__repo_list

    def clean(self):
        for r in self.__repo_list:
            ic = ImageCleaner(self.__access_key_id, self.__access_key_secret, self.__region_id,
                              self.__namespace, r['repoName'], self.__days_keep, self.__tags_keep,
                              self.__tag_prefix)
            ic.clean()


class ImageCleaner:

    def __init__(self, access_key_id, access_key_secret, region_id, namespace, repo, days_keep=10, tags_keep=10,
                 tag_prefix=""):

        self.__tag_list = list()
        self.__total = 0
        self.__page = 1
        self.__page_count = 0
        self.__page_size = 30
        self.__tag_id = 0
        self.__access_key_id = access_key_id
        self.__access_key_secret = access_key_secret
        self.__region_id = region_id
        self.__namespace = namespace
        self.__repo = repo
        self.__days_keep = int(days_keep)
        self.__tags_keep = int(tags_keep)
        now_time = (datetime.datetime.now() - datetime.timedelta(days=self.__days_keep))
        self.__keep_timestamp = int(time.mktime(now_time.timetuple())) * 1000
        self.__keep_time_string = now_time.strftime("%Y-%m-%d %H:%M:%S")
        self.__tag_prefix = tag_prefix
        self.get_full_repo_tags()

    def get_repo_tags(self):
        api_client = AcsClient(self.__access_key_id, self.__access_key_secret, self.__region_id)
        request = GetRepoTagsRequest.GetRepoTagsRequest()
        request.set_RepoNamespace(self.__namespace)
        request.set_endpoint("cr.%s.aliyuncs.com" % self.__region_id)
        request.set_RepoName(self.__repo)
        request.set_Page(self.__page)
        request.set_PageSize(self.__page_size)
        try:
            response = api_client.do_action_with_exception(request)
            response_dict = json.loads(str(response, 'utf8'))
            if not self.__page_count:
                self.__total = response_dict['data']['total']
                self.__page_count = response_dict['data']['total'] // self.__page_size
            for tag in response_dict['data']['tags']:
                # print(tag['tag'])
                if tag['imageUpdate'] > self.__keep_timestamp:
                    print(datetime.datetime.fromtimestamp(tag['imageUpdate'] / 1000).strftime(
                        "%Y-%m-%d %H:%M:%S") + " is after " + self.__keep_time_string + " , skip")
                    continue
                if self.__tag_prefix and not tag['tag'].startswith(self.__tag_prefix):
                    print("prefix is not %s, skip" % self.__tag_prefix)
                    continue
                self.__tag_list.append(tag)

        except ServerException as e:
            if e.http_status == 404:
                print("Repo '%s' in namespace '%s' in region '%s' is not exist." % (
                    self.__repo, self.__namespace, self.__region_id))
                sys.exit(2)
            else:
                print(e)
        except ClientException as e:
            print(e)

    def get_full_repo_tags(self):
        while True:
            self.get_repo_tags()
            self.__page += 1
            if self.__page > self.__page_count:
                break
        print("repo %s in namespace %s have %s tags" % (self.__repo, self.__namespace, self.__total))
        return self.__tag_list

    def delete_image(self, tag):
        api_client = AcsClient(self.__access_key_id, self.__access_key_secret, self.__region_id)
        request = DeleteImageRequest.DeleteImageRequest()
        request.set_RepoNamespace(self.__namespace)
        request.set_endpoint("cr.%s.aliyuncs.com" % self.__region_id)
        request.set_RepoName(self.__repo)
        request.set_Tag(tag)
        try:
            api_client.do_action_with_exception(request)
        except ServerException as e:
            print(e)
        except ClientException as e:
            print(e)

    def clean(self):
        print("clean image '%s' tags in namespace '%s' before %s days ago(%s) or more than %s tags" % (
            self.__repo, self.__namespace, str(self.__days_keep), self.__keep_time_string, str(self.__tags_keep)))
        self.__tag_id = 0
        for tag_item in self.__tag_list:
            self.__tag_id += 1
            if self.__tag_id >= self.__tags_keep:
                update_time = datetime.datetime.fromtimestamp(tag_item['imageUpdate'] / 1000).strftime(
                    "%Y-%m-%d %H:%M:%S")
                print("delete tag '%s' update on '%s'" % (tag_item['tag'], update_time))
                self.delete_image(tag_item['tag'])


if __name__ == '__main__':
    argv = sys.argv[1:]
    accessKeyId = settings.accessKeyId
    accessKeySecret = settings.accessKeySecret
    regionId = settings.regionId
    nameSpace = settings.namespace
    repoName = settings.repo
    daysKeep = settings.days_keep
    tagsKeep = settings.tags_keep
    tagPrefix = settings.tag_prefix
    help_text = """
    Usage: 
        clean repo:
        cleaner.py [OPTION] <namespace> <repo name>
        clean all repos in namespace:
        cleaner.py [OPTION] <namespace>
    or set namespace, repo name, and other options from environment var
    
    OPTIONS:
        opt             env var             description
        -i  --id            AccessKeyId         set accessKeyId
        -s  --secret        AccessKeySecret     set accessKeySecret
        -r  --region        RegionId            set regionId. Such as 'cn-hangzhou', 'cn-shanghai' .etc. default 'cn-hangzhou'
        -d  --days          DaysKeep            set DaysKeep. How many days keep from clean, default 10
        -t  --tags          TagsKeep            set TagsKeep. How many tags keep from clean, default 10
        -p  --tag-prefix    TagPrefix           set TagPrefix. Filter tag list with tag prefix
                            Namespace           set Namespace. The namespace of repo need be clean
                            Repo                set Repo. The name of repo need be clean
    """
    try:
        opts, args = getopt.getopt(argv, "hi:s:r:d:t:p:", ["help", "id=", "secret=", "region=", "days=", "tags="])
    except getopt.GetoptError:
        print(help_text)
        sys.exit(2)
    for opt, arg in opts:
        if opt == '-h':
            print(help_text)
            sys.exit()
        elif opt in ("-i", "--id"):
            accessKeyId = arg
        elif opt in ("-s", "--secret"):
            accessKeySecret = arg
        elif opt in ("-r", "--region"):
            regionId = arg
        elif opt in ("-d", "--days"):
            daysKeep = arg
        elif opt in ("-t", "--tags"):
            tagsKeep = arg
        elif opt in ("-p", "--tag-prefix"):
            tagPrefix = arg

    if len(args) > 0:
        nameSpace = args[0]
    if len(args) > 1:
        repoName = args[1]
    if repoName:
        image_cleaner = ImageCleaner(accessKeyId, accessKeySecret, regionId, nameSpace, repoName, daysKeep, tagsKeep,
                                     tagPrefix)
        image_cleaner.clean()
    else:
        namespace_cleaner = NamespaceCleaner(accessKeyId, accessKeySecret, regionId, nameSpace, daysKeep, tagsKeep,
                                             tagPrefix)
        namespace_cleaner.clean()
